import 'dart:typed_data';

import 'package:encrypt/encrypt.dart';
import 'package:lib_database/src/model/dapp_token_model/dapp_token_model.dart';
import 'package:lib_util/lib_util.dart';

// --- lib_network
import 'src/wallet.dart';
import 'src/network.dart';
import 'sp.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

import 'objectbox.g.dart'; // created by `flutter pub run build_runner build`

import 'package:lib_database/src/model/basemodel.dart';

// dapp favitior model
import 'package:lib_database/src/model/favorite_dapp_model/favorite_dapp_model.dart';
import 'package:lib_database/src/model/dapp_cache_model/dapp_cache_model.dart';
import 'package:lib_database/src/model/wallet_model/wallet_model.dart';
import 'package:lib_database/src/model/wallet_info_model/wallet_info_model.dart';
import 'package:lib_database/src/model/token_model/token_model.dart';
import 'package:lib_database/src/model/node_list_model/node_list_model.dart';
import 'package:lib_database/src/model/user_settings_model/user_settings_model.dart';
import 'package:lib_database/src/model/custom_evm_chain/custom_evm_chain.dart';
import 'package:lib_database/src/model/transaction_model/transaction_model.dart';

class ObjectBox {
  late final Store store;
  late final Box<Wallet> walletBox;
  late final BaseModel<Wallet> _walletModel;

  late final Box<WalletInfo> walletInfoBox;
  late final BaseModel<WalletInfo> _walletInfoModel;

  late final Box<Token> tokenBox;
  late final BaseModel<Token> _tokenModel;

  late final Box<DappToken> dappBox;
  late final BaseModel<DappToken> _dappTokenModel;

  late final Box<UserSettings> _userSettingsBox;
  late final BaseModel<UserSettings> _useSettingsModel;

  // 收藏 列表
  late final Box<FavoriteDApp> _favoriteDAppBox;
  late final BaseModel<FavoriteDApp> _favoriteDAppModel;

  // 缓存 列表
  late final Box<CacheDApp> _cacheDAppBox;
  late final BaseModel<CacheDApp> _cacheDAppModel;

  late final Box<NodeInfo> _nodeInfoBox;
  late final BaseModel<NodeInfo> _nodeInfoModel;

  late final Box<TransactionBean> _transactionBox;
  late final BaseModel<TransactionBean> _transactionModel;

  late final Box<CustomEVMChain> _customEVMChainBox;
  late final BaseModel<CustomEVMChain> _customEVMChainModel;

  WalletModel get walletModel => _walletModel as WalletModel;

  WalletInfoModel get walletInfoModel => _walletInfoModel as WalletInfoModel;

  TokenModel get tokenModel => _tokenModel as TokenModel;
  DappTokenModel get dappTokenModel => _dappTokenModel as DappTokenModel;

  UserSettingsModel get userSettingsModel =>
      _useSettingsModel as UserSettingsModel;

  FavoriteDAppModel get favoriteDAppModel =>
      _favoriteDAppModel as FavoriteDAppModel;

  CacheDAppModel get cacheDAppModel => _cacheDAppModel as CacheDAppModel;

  NodeInfoModel get nodeInfoModel => _nodeInfoModel as NodeInfoModel;

  TransactionModel get transactionModel =>
      _transactionModel as TransactionModel;

  CustomEVMChainModel get customEVMChainModel =>
      _customEVMChainModel as CustomEVMChainModel;

  ObjectBox._privateConstructor();

  static final ObjectBox _instance = ObjectBox._privateConstructor();

  factory ObjectBox() {
    return _instance;
  }

  /// Create an instance of ObjectBox to use throughout the app.
  Future<Store> create() async {
    final docsDir = await getApplicationDocumentsDirectory();
    // Future<Store> openStore() {...} is defined in the generated objectbox.g.dart
    final store = await openStore(directory: p.join(docsDir.path, "obx-kai"));
    _create(store);

    return store;
  }

  ///通过引用方式访问数据库
  void referenceStore(ByteData reference) {
    // var refUTF8 = utf8.encode(reference);
    // ByteData refData = ByteData.sublistView(refUTF8);
    final store = Store.fromReference(getObjectBoxModel(), reference);
    return _create(store);
  }

  _create(store) {
    // Add any additional setup code, e.g. build queries.
    walletBox = Box<Wallet>(store);
    _walletModel = WalletModel(walletBox);

    walletInfoBox = Box<WalletInfo>(store);
    _walletInfoModel = WalletInfoModel(walletInfoBox);

    tokenBox = Box<Token>(store);
    _tokenModel = TokenModel(tokenBox);

    dappBox = Box<DappToken>(store);
    _dappTokenModel = DappTokenModel(dappBox);

    _userSettingsBox = Box<UserSettings>(store);
    _useSettingsModel = UserSettingsModel(_userSettingsBox);

    _favoriteDAppBox = Box<FavoriteDApp>(store);
    _favoriteDAppModel = FavoriteDAppModel(_favoriteDAppBox);

    _cacheDAppBox = Box<CacheDApp>(store);
    _cacheDAppModel = CacheDAppModel(_cacheDAppBox);

    _nodeInfoBox = Box<NodeInfo>(store);
    _nodeInfoModel = NodeInfoModel(_nodeInfoBox);

    _transactionBox = Box<TransactionBean>(store);
    _transactionModel = TransactionModel(_transactionBox);

    _customEVMChainBox = Box<CustomEVMChain>(store);
    _customEVMChainModel = CustomEVMChainModel(_customEVMChainBox);
  }

  // 更新BTC （钱包模型） 地址 以及更新 （代币 模型）
  updateBtcWalletInfoAddress(int walletInfoId, String address) {
    WalletInfo? walletInfo = walletInfoModel.getWalletInfoById(walletInfoId);
    if (null == walletInfo || !walletInfo.getChain()!.isBtcOrBtcTestnet()) {
      return;
    }

    List<int> tokenIds = tokenBox
        .query(Token_.walletInfoId.equals(walletInfoId))
        .build()
        .findIds();
    if (tokenIds.isNotEmpty) {
      tokenBox.removeMany(tokenIds);
      for (var tId in tokenIds) {
        List<int> transactionIds = _transactionBox
            .query(TransactionBean_.localTokenId.equals(tId))
            .build()
            .findIds();
        if (transactionIds.isNotEmpty) {
          _transactionBox.removeMany(transactionIds);
        }
      }
    }

    walletInfo.address = address;
    walletInfoBox.put(walletInfo, mode: PutMode.update);
  }

  //   删除 （钱包信息列表），关联 （钱包模型） 地址
  bool deleteWallet(int walletId) {
    List<int> infoIdList = walletInfoBox
        .query(WalletInfo_.walletId.equals(walletId))
        .build()
        .findIds();
    for (var infoId in infoIdList) {
      deleteWalletInfo(infoId);
    }
    bool isDelSuccess=walletBox.remove(walletId);
    return isDelSuccess;
  }

  // 删除 （钱包代币 模型） （删除交易记录模型）
  void deleteWalletInfo(int walletInfoId) {
    List<int> tokenIds = tokenBox
        .query(Token_.walletInfoId.equals(walletInfoId))
        .build()
        .findIds();
    if (tokenIds.isNotEmpty) {
      tokenBox.removeMany(tokenIds);
      for (var tId in tokenIds) {
        List<int> transactionIds = _transactionBox
            .query(TransactionBean_.localTokenId.equals(tId))
            .build()
            .findIds();
        if (transactionIds.isNotEmpty) {
          _transactionBox.removeMany(transactionIds);
        }
      }
    }
    walletInfoBox.remove(walletInfoId);
  }

  // 获取 当前钱包信息
  WalletInfo? getCurrentWalletInfo(String uid) {
    try {
      // 钱包 唯一
      Wallet? wallet = walletModel.getWalletByUId(uid);
      if (wallet == null) {
        return null;
      }
      // 根据 链 生成 多个 在选择 链 中选中 即留下 存储 sp
      int currentWalletId = SP().getWalletSelectedInfoId(wallet.id);
      WalletInfo? walletInfo = null;
      if (currentWalletId != 0) {
        walletInfo = walletInfoModel.getWalletInfoById(currentWalletId);
      } else {
        walletInfo = walletInfoModel.getFirstWalletInfo(wallet);
        if (walletInfo != null) {
          SP().setWalletSelectedInfoId(wallet.id, walletInfo!.id);
        }
      }
      return walletInfo;
    } catch (e) {
      return null;
    }
  }

  // 通过 链 获取 钱包信息
  List<WalletInfo> getWalletInfoListByChain(Chain c, {bool showWatch = true}) {
    if (showWatch) {
      return walletInfoBox
          .query(WalletInfo_.chainShortName.equals(c.shortName))
          .build()
          .find();
    } else {
      List<int> watchWalletIds =
          walletBox.query(Wallet_.type.equals(2)).build().findIds();
      return walletInfoBox
          .query(WalletInfo_.chainShortName.equals(c.shortName))
          .build()
          .find()
          .where((element) => !watchWalletIds.contains(element.walletId))
          .toList(growable: false);
    }
  }

  // // 创建观察 钱包
  // Future<List<WalletInfo>> createWatchWallet(
  //   String watchAddress,
  //   List<Chain> chains,
  //   String uId, {
  //   String? walletName,
  //   bool isMPC = false,
  //   String salt = '',
  // }) async {
  //   String key = isMPC ? watchAddress : watchAddress.toLowerCase();
  //   if (isMPC == false && key.startsWith('0x')) {
  //     key = key.substring(2);
  //   }

  //   //AES加密
  //   var encryptedKey = isMPC ? key : _encryptKey(key);

  //   Wallet? wallet = walletBox
  //       .query(Wallet_.key.equals(encryptedKey) & Wallet_.uId.equals(uId))
  //       .build()
  //       .findFirst();
  //   if (null != wallet) {
  //     for (Chain c in chains) {
  //       WalletInfo? info = walletInfoModel.getWalletInfoByWalletIdAndChain(
  //           wallet.id, c.shortName);
  //       if (null != info) {
  //         return Future.error('钱包已经存在，请勿重复导入');
  //       }
  //     }
  //   }

  //   int walletId = null != wallet
  //       ? wallet.id
  //       : walletBox.put(Wallet(
  //           uId: uId, key: encryptedKey, type: 2, name: walletName)); //2为观察钱包

  //   var time = DateTime.now().millisecondsSinceEpoch;
  //   List<WalletInfo> infoList = [];

  //   String pk = isMPC == true ? watchAddress : '';
  //   // String address = watchAddress.toLowerCase();
  //   for (var c in chains) {
  //     WalletInfo walletInfo = WalletInfo(
  //         walletId: walletId,
  //         createTime: time,
  //         privateKey: pk,
  //         chainShortName: c.shortName,
  //         address: isMPC == true
  //             ? WalletUtils().generateMPCAddress(
  //                 Mpc().mpcFragmentGetPublicKey(watchAddress), c)
  //             : watchAddress);
  //     infoList.add(walletInfo);
  //   }

  //   if (infoList.isNotEmpty) {
  //     walletInfoBox.putMany(infoList);
  //   }

  //   return infoList;
  // }

  // // 创建钱包
  // Future<List<WalletInfo>> createWallet(
  //     String key, List<Chain> chains, String uId,
  //     {String? walletName,
  //     bool isMPC = false,
  //     String? salt = '',
  //     bool? backup = false}) async {
  //   key = isMPC == true ? key : key.toLowerCase();
  //   if (key.startsWith('0x') && isMPC != true) {
  //     key = key.substring(2);
  //   }

  //   //AES加密
  //   var encryptedKey = isMPC == true ? key : _encryptKey(key);

  //   Wallet? wallet = walletBox
  //       .query(Wallet_.key.equals(encryptedKey) & Wallet_.uId.equals(uId))
  //       .build()
  //       .findFirst();
  //   if (null != wallet) {
  //     for (Chain c in chains) {
  //       WalletInfo? info = walletInfoModel.getWalletInfoByWalletIdAndChain(
  //           wallet.id, c.shortName);
  //       if (null != info) {
  //         return Future.error(
  //             'The wallet already exists, please do not import it again.');
  //       }
  //     }
  //   }

  //   bool isMnemonic = key.contains(' ');

  //   int walletId = null != wallet
  //       ? wallet.id
  //       : walletBox.put(Wallet(
  //           salt: salt,
  //           uId: uId,
  //           key: encryptedKey,
  //           type: isMPC == true ? 3 : (isMnemonic ? 0 : 1),
  //           name: walletName,
  //           backup: backup));

  //   var time = DateTime.now().millisecondsSinceEpoch;
  //   List<WalletInfo> infoList = [];

  //   String evmKey = '';
  //   String evmAddress = '';
  //   for (var c in chains) {
  //     String pk;
  //     String address;
  //     if (c.type == ChainType.eth) {
  //       if (evmKey.isEmpty || evmAddress.isEmpty) {
  //         evmKey = isMPC == true
  //             ? key
  //             : (isMnemonic
  //                 ? await WalletUtils().generatePkByMnemonic(key, c)
  //                 : key);
  //         evmAddress = isMPC == true
  //             ? WalletUtils().generateMPCAddress(
  //                 Mpc().mpcFragmentGetPublicKey(evmKey.split(',')[0]), c)
  //             : WalletUtils().generateAddress(evmKey, c);
  //       }
  //       print('evmAddress $evmAddress');
  //       pk = evmKey;
  //       address = evmAddress;
  //     } else {
  //       pk = isMPC == true
  //           ? key
  //           : (isMnemonic
  //               ? await WalletUtils().generatePkByMnemonic(key, c)
  //               : key);
  //       address = isMPC == true
  //           ? WalletUtils().generateMPCAddress(
  //               Mpc().mpcFragmentGetPublicKey(evmKey.split(',')[0]), c)
  //           : WalletUtils().generateAddress(evmKey, c);
  //     }
  //     if (pk.isNotEmpty && address.isNotEmpty) {
  //       WalletInfo walletInfo = WalletInfo(
  //           walletId: walletId,
  //           createTime: time,
  //           privateKey: isMPC == true ? pk : _encryptKey(pk),
  //           chainShortName: c.shortName,
  //           address: address);
  //       infoList.add(walletInfo);
  //       // print(walletInfo);
  //     }
  //   }

  //   if (infoList.isNotEmpty) {
  //     walletInfoBox.putMany(infoList);
  //   }

  //   return infoList;
  // }

  String? getKeyByWallet(Wallet wallet) {
    String key = wallet.key;
    if (key.isNotEmpty) {
      return _decryptKey(key);
    }
    return key;
  }

  String? getKeyByWalletInfo(WalletInfo walletInfo) {
    String key = walletInfo.privateKey;
    if (key.isNotEmpty) {
      return _decryptKey(key);
    }
    return key;
  }

  static final Key _aesKey = Key.fromUtf8('wKJXezVZyYZJaWSP3P0krPGb2ymD6KQg');
  static final IV _iv = IV.fromUtf8('zJBrKbEehdfXvLCp');
  static final _encrypt = Encrypter(AES(_aesKey));

  String _encryptKey(String key) {
    var encrypted = _encrypt.encrypt(key, iv: _iv);
    String encryptedStr = encrypted.base64;
    // var decrypted =
    //     _encrypt.decrypt(Encrypted.fromBase64(encryptedStr), iv: _iv);
    return encryptedStr;
  }

  String _decryptKey(String encryptedStr) {
    var decrypted =
        _encrypt.decrypt(Encrypted.fromBase64(encryptedStr), iv: _iv);
    return decrypted;
  }

  String decryptWalletKey(String encryptedStr) {
    return _decryptKey(encryptedStr);
  }

  String encryptWalletKey(String encryptedStr) {
    return _encryptKey(encryptedStr);
  }
}
